package com.shareware.common.secure.aes

import com.shareware.common.utils.hexToByteArray
import com.shareware.common.utils.log
import com.shareware.common.utils.toHexString
import java.security.GeneralSecurityException
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */
class AesGcm private constructor() {

    companion object {
        private const val ALGORITHM = "AES"
        private const val TRANSFORMATION = "AES/GCM/NoPadding"
        private const val TAG = "AesGcm"

        //AES密钥长度为128bit、192bit、256bit，默认为128bit，一个字节8位
        private const val KEY_LENGTH_DEFAULT = 16

        //使用随机数产生的初始化向量才能达到语义安全
        private const val IV_RANDOM_SIZE = 12

        fun encrypt(content: String, key: String): String {
            return if (content.isNotEmpty() && key.isNotEmpty()) {
                val keyBytes = key.hexToByteArray()
                if (keyBytes.size < KEY_LENGTH_DEFAULT) {
                    log(TAG, "key length is not right")
                    ""
                } else {
                    encrypt(content, keyBytes)
                }
            } else {
                ""
            }
        }

        fun encrypt(content: String, keyBytes: ByteArray): String {
            return if (content.isNotEmpty() && keyBytes.size >= KEY_LENGTH_DEFAULT) {
                val ivBytes = generateSecureRandom()
                val result = encrypt(content.toByteArray(), keyBytes, ivBytes)
                if (result.isEmpty()) {
                    ""
                } else {
                    ivBytes.toHexString() + result.toHexString()
                }
            } else {
                ""
            }
        }

        fun encrypt(contentBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): ByteArray {
            return if (contentBytes.isNotEmpty() && keyBytes.size >= KEY_LENGTH_DEFAULT && ivBytes.size >= IV_RANDOM_SIZE) {
                try {
                    val secretKeySpec = SecretKeySpec(keyBytes, ALGORITHM)
                    val cipher = Cipher.getInstance(TRANSFORMATION)
                    val algorithmParameterSpec = GCMParameterSpec(128, ivBytes)
                    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, algorithmParameterSpec)
                    cipher.doFinal(contentBytes)
                } catch (ex: GeneralSecurityException) {
                    byteArrayOf()
                }
            } else {
                byteArrayOf()
            }
        }

        fun decrypt(source: String, key: String): String {
            return if (source.isNotEmpty() && key.isNotEmpty()) {
                val keyBytes = key.hexToByteArray()
                if (keyBytes.size < KEY_LENGTH_DEFAULT) {
                    log(TAG, "key length is not right")
                    ""
                } else {
                    decrypt(source, keyBytes)
                }
            } else {
                ""
            }
        }

        fun decrypt(data: String, keyBytes: ByteArray): String {
            return if (data.isNotEmpty() && keyBytes.size >= KEY_LENGTH_DEFAULT) {
                try {
                    val secretKeySpec = SecretKeySpec(keyBytes, ALGORITHM)
                    val cipher = Cipher.getInstance(TRANSFORMATION)
                    val ivString = getIvOrSource(data, true)
                    val result = getIvOrSource(data, false)
                    if (ivString.isNotEmpty() && result.isNotEmpty()) {
                        val algorithmParameterSpec =
                            GCMParameterSpec(128, ivString.hexToByteArray())
                        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, algorithmParameterSpec)
                        cipher.doFinal(result.hexToByteArray()).toString(Charsets.UTF_8)
                    } else {
                        log(TAG, "ivParameter or source is null")
                        ""
                    }
                } catch (ex: Exception) {
                    log(TAG, "GCM decrypt data exception: ${ex.message}")
                    ""
                }
            } else {
                ""
            }
        }

        fun decrypt(dataBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): String {
            return if (dataBytes.isNotEmpty() && keyBytes.size >= KEY_LENGTH_DEFAULT) {
                try {
                    val secretKeySpec = SecretKeySpec(keyBytes, ALGORITHM)
                    val cipher = Cipher.getInstance(TRANSFORMATION)
                    val algorithmParameterSpec = GCMParameterSpec(128, ivBytes)
                    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, algorithmParameterSpec)
                    cipher.doFinal(dataBytes).toString(Charsets.UTF_8)
                } catch (ex: Exception) {
                    log(TAG, "GCM decrypt data exception: ${ex.message}")
                    ""
                }
            } else {
                ""
            }
        }

        private fun getIvOrSource(source: String, iv: Boolean): String {
            val size = IV_RANDOM_SIZE * 2
            return if (source.isNotEmpty() && source.length >= size) {
                if (iv) {
                    source.substring(0, size)
                } else {
                    source.substring(size)
                }
            } else {
                log(TAG, "IV is invalid")
                ""
            }
        }

        private fun generateSecureRandom(): ByteArray {
            return try {
                val random = SecureRandom()
                val numBytes = ByteArray(IV_RANDOM_SIZE)
                random.nextBytes(numBytes)
                numBytes
            } catch (ignore: Exception) {
                byteArrayOf()
            }
        }
    }
}